#include "link.h"

Link::Link(const char *name,  const char *lat, long double lat_offset, const char *bw, long double bw_offset, const char *bw_lim, long double bw_lim_offset, const char *lat_error, const char *bw_error, const char *lat_forecast, const char *bw_forecast, const char *bw_lim_forecast) : Resource(name) {

	if (( lat!=NULL && FileChecker::verify(lat)) || FileChecker::verify(bw) || FileChecker::verify(bw_lim) || (lat_forecast!=NULL && FileChecker::verify(lat_forecast)) || (bw_forecast!=NULL && FileChecker::verify(bw_forecast)) || (bw_lim_forecast!=NULL && FileChecker::verify(bw_lim_forecast)) ) {
		throw std::runtime_error("Cannot open trace/forecast file(s). It could be latency, bandwidth or bandwith_limit\n");
	}
	
	if( (lat_error != NULL && FileChecker::verify(lat_error)) ) {
		throw std::runtime_error("Cannot open latency error trace\n");
	}
	
	if( bw_error == NULL || FileChecker::verify(bw_error) ) {
		throw std::runtime_error("Cannot open bandwidth error trace\n");
	}

	this->resource = SG_newTCPLink(name, (char *)lat, lat_offset,MINIMUM_LATENCY,bw, bw_offset, 0,0.0,bw_lim, bw_lim_offset, 0,NULL);
	this->setBandwidthLimitOffset(bw_lim_offset);
	
	this->lat_error = NULL;
	if( lat_error != NULL ) {
		this->lat_error = new char[strlen(lat_error)+1];
		this->lat_error = strcpy((char *)this->lat_error, lat_error);
		forecast_lat_error = SG_newTrace(SG_SINGLE_LEVEL, SG_DYNAMIC, this->lat_error, 0.0, TRACE_TIMESTEP);		
	}
	
	this->lat_forecast = NULL;
	if (lat_forecast!=NULL) {
		this->lat_forecast = new char[strlen(lat_forecast)+1];
		this->lat_forecast = strcpy((char *)this->lat_forecast, lat_forecast);
		forecast_lat = SG_newTrace(SG_SINGLE_LEVEL, SG_DYNAMIC, this->lat_forecast,0.0,TRACE_TIMESTEP);		
	}
	
	this->bw_forecast = NULL;
	if (bw_forecast!=NULL) {
		this->bw_forecast = new char[strlen(bw_forecast)+1];
		this->bw_forecast = strcpy((char *)this->bw_forecast, bw_forecast);
		forecast_bw = SG_newTrace(SG_SINGLE_LEVEL, SG_DYNAMIC, this->bw_forecast,0.0,TRACE_TIMESTEP);		
	}
	
	this->bw_lim_forecast = NULL;
	if (bw_lim_forecast!=NULL) {
		this->bw_lim_forecast = new char[strlen(bw_lim_forecast)+1];
		this->bw_lim_forecast = strcpy((char *)this->bw_lim_forecast, bw_lim_forecast);
		forecast_bw_lim = SG_newTrace(SG_SINGLE_LEVEL, SG_DYNAMIC, this->bw_lim_forecast,0.0,TRACE_TIMESTEP);		
	}
	


	this->lat_trace = NULL;			
	if (lat!=NULL) {
		this->lat_trace = new char[strlen(lat)+1];
		this->lat_trace = strcpy((char *)this->lat_trace, lat);
	}

	this->bw_trace = new char[strlen(bw)+1];
	this->bw_trace = strcpy((char *)this->bw_trace, bw);

	this->bw_error = new char[strlen(bw_error)+1];
	this->bw_error = strcpy((char *)this->bw_error, bw_error);
	forecast_bw_error = SG_newTrace(SG_SINGLE_LEVEL, SG_DYNAMIC, this->bw_error, 0.0, TRACE_TIMESTEP);
	
	this->bw_lim_trace = new char[strlen(bw_lim)+1];
	this->bw_lim_trace = strcpy((char *)this->bw_lim_trace, bw_lim);
	
}

Link::Link(const char *name, long double lat,  long double bw, long double bw_lim) : Resource(name) {
	this->resource = SG_newTCPLink(name, NULL, 0, lat,NULL, 0, bw,0.0,NULL, 0, bw_lim,NULL);	

	this->lat_trace = NULL;
	this->bw_lim_trace = NULL;
	this->bw_trace = NULL;

	this->lat_forecast = NULL;
	this->bw_lim_forecast = NULL;
	this->bw_forecast = NULL;
	
}

Link::~Link() {
	if (this->lat_trace) delete this->lat_trace;
	if (this->bw_trace) delete this->bw_trace;
	if (this->bw_lim_trace) delete this->bw_lim_trace;

	if (this->lat_forecast) delete this->lat_forecast;
	if (this->bw_forecast) delete this->bw_forecast;
	if (this->bw_lim_forecast) delete this->bw_lim_forecast;
	
//	if (forecast_lat) free(forecast_lat);
//	if (forecast_bw) free(forecast_bw);
//	if (forecast_bw_lim) free(forecast_bw_lim);		

}

long double Link::getLatency(long double time) {
	
	
	long double latency = SG_getLinkLatency(this->resource, time);
	
	if ((!this->isPerformanceConstant()) && (this->lat_forecast!=NULL)) {
		if (time==-1) time = SG_getClock();

		latency = SG_getTraceValueWithOffset(forecast_lat, time, this->getLatencyOffset());		

	}
	
	return latency;
}

long double Link::getLatencyError(long double time) {
	long double error = 0;
	if ((!this->isPerformanceConstant()) && (this->lat_error != NULL)) {
		if (time==-1) time = SG_getClock();
		error = SG_getTraceValueWithOffset(forecast_lat_error, time, this->getLatencyOffset());
	}
	return error;
}

long double Link::getBandwidthError(long double time) {
	long double error = 0;
	if ((!this->isPerformanceConstant()) && (this->bw_error != NULL)) {
		if (time==-1) time = SG_getClock();
		error = SG_getTraceValueWithOffset(forecast_bw_error, time, this->getBandwidthOffset());
	}
	return error;
}

long double Link::getBandwidth(long double time) {
	
	long double bw = SG_getLinkBandwidth(this->resource, time);
	
	if ((!this->isPerformanceConstant()) && (this->bw_forecast!=NULL)) {
		if (time==-1) time = SG_getClock();
		bw = SG_getTraceValueWithOffset(forecast_bw, time, this->getBandwidthOffset());		

	}
	
	return bw;
}

long double Link::getBandwidthLimit(long double time) {

	
	long double bw_lim = SG_getLinkMaxBandwidth(this->resource, time);
	
	if ((!this->isPerformanceConstant()) && (this->bw_lim_forecast!=NULL)) {
		if (time==-1) time = SG_getClock();

		bw_lim = SG_getTraceValueWithOffset(forecast_bw_lim, time, this->getBandwidthLimitOffset());		

	}
	
	return bw_lim;
}

const char *Link::getLatencyTrace() {
	return this->lat_trace;
}

const char *Link::getLatencyError() {
	return this->lat_error;
}

const char *Link::getBandwidthError() {
	return this->bw_error;
}

const char *Link::getLatencyForecast() {
	return this->lat_forecast;
}

const char *Link::getBandwidthTrace() {
	return this->bw_trace;
}

const char *Link::getBandwidthForecast() {
	return this->bw_forecast;
}

const char *Link::getBandwidthLimitTrace() {
	return this->bw_lim_trace;
}

const char *Link::getBandwidthLimitForecast() {
	return this->bw_lim_forecast;
}

long double Link::getBandwidthAvailable(long double time) {

	long double bw = this->getBandwidth(time);
	long double bw_lim = this->getBandwidthLimit(time);
	
	long double bw_available = bw;
	
	//Verify if the bandwidth limit was reached!
	SG_Task *active_transfers = SG_getTasksRunningOnResource(this->getSGResource());	

	int total_transfers = 1;
	
	int n = 0;
	while (active_transfers[n]) {
		total_transfers++;
		n++;
	}
	
	//TEST-INI
	SG_Task *sched_transfers = SG_getTasksScheduledOnResource(this->getSGResource());	
	n = 0;
	while (sched_transfers[n]) {
		total_transfers++;
		n++;
	}

    if ( total_transfers * bw >= bw_lim) {
    	bw_available = bw_lim/total_transfers;
    }
    
    free(sched_transfers);
    //TEST-FIN
    
    //Release memory
    free(active_transfers);
    /////
    
    return bw_available;
}

void Link::setLatencyOffset(long double new_offset) {
	this->resource->latency_offset = new_offset;
}

long double Link::getLatencyOffset() {
	return this->resource->latency_offset;
}

void Link::setBandwidthOffset(long double new_offset) {
	this->resource->bandwidth_offset = new_offset;
}

long double Link::getBandwidthOffset() {
	return this->resource->bandwidth_offset;
}

void Link::setBandwidthLimitOffset(long double new_offset) {
	this->resource->max_bandwidth_offset = new_offset;
}

long double Link::getBandwidthLimitOffset() {
	return this->resource->max_bandwidth_offset;
}
